#ifndef __NCACHE_H__
#define __NCACHE_H__

#include <semaphore.h>

#include "list.h"
#include "worker.h"
#include "ylock.h"
#include "plock.h"
#include "dbg.h"

/**
 * @file Cache with ref
 *
 * 默认情况下，cache项常驻内存，引用计数为0时，并不会drop。
 * 这是适应volume_proto等处的用法的。
 *
 * 如果想在引用计数为0时，释放cache项，需要先行调用mcache_drop/mcache_drop_nolock。
 * 比如在控制器发生切换的时候。
 */

#define MCACHE_SIZE (32768*3)
//#define CACHE_DROP_DEBUG 

typedef struct {
        uint32_t curlen;
        uint8_t _private;
        union {
                sy_rwlock_t rwlock;
                plock_t plock;
        } u;
        struct list_head head;
} mcache_head_t;

typedef struct {
        struct list_head hook;
        sy_spinlock_t ref_lock;
        int16_t ref; /*ref count*/
        char erase;
        char __erase__;
        char _private;

#ifdef CACHE_DROP_DEBUG
        time_t erase_time;
        struct list_head ref_list;
#endif

        union {
                sy_rwlock_t rwlock;
                plock_t plock;
        } u;

        void *head;
        void *value;
        void *group;
} mcache_entry_t;

#ifdef CACHE_DROP_DEBUG
typedef struct {
        struct list_head hook;
        task_t ownner;
        int count;
} mcache_ref_t;
#endif

//typedef int (*replace_func)(void **old, void **new);
typedef int (*drop_func)(void *, mcache_entry_t *cent, int recycle);
typedef uint32_t (*hash_func)(const void *);
typedef int (*cmp_func)(const void *,const void *);
typedef void (*exec_func)(void *, void *);
typedef int (*exec_func_r)(void *, void *);

typedef struct {
        drop_func drop;

        sy_spinlock_t size_lock;
        uint64_t max_entry; /*how many entrys can this cache hold*/
        uint64_t entry;
        int array_len; /*length of the array below*/
        mcache_head_t array[0];
} mcache_group_t;

typedef struct {
        char name[MAX_NAME_LEN];
        char _private;
        hash_func hash;
        hash_func group_hash;
        cmp_func cmp;
        worker_handler_t timer_handler;
        mcache_group_t *group[0];
} mcache_t;

int mcache_init(mcache_t **cache, uint64_t max_entry, cmp_func cmp,
                hash_func hash, hash_func group_hash, drop_func drop, int private,
                const char *name);

int mcache_get(mcache_t *cache, const void *key, mcache_entry_t **ent);
void mcache_release(mcache_entry_t *ent);

int mcache_ref(mcache_entry_t *ent);

int mcache_rdlock(mcache_entry_t *ent);
int mcache_tryrdlock(mcache_entry_t *ent);

int mcache_wrlock(mcache_entry_t *ent);
int mcache_trywrlock(mcache_entry_t *ent);
int mcache_wrlock_prio(mcache_entry_t *ent, int prio);

void mcache_unlock(mcache_entry_t *ent);

int mcache_lockcache(mcache_t *cache, const void *key);
void mcache_unlockcache(mcache_t *cache, const void *key);

void mcache_iterator(mcache_t *cache, void (*callback)(void *, void *), void *arg);

int mcache_insert(mcache_t *cache, const void *key, void *value);
int mcache_insert_lock(mcache_t *cache, const void *key, mcache_entry_t **_ent);
void mcache_insert_unlock(mcache_t *cache, const void *key, mcache_entry_t *ent);
int mcache_insert_wrlock(mcache_t *cache, const void *key, void *value, mcache_entry_t **_ent);

int mcache_drop(mcache_entry_t *ent);
int mcache_drop_nolock(mcache_entry_t *ent);
void mcache_remove_unlock(mcache_entry_t *ent);

#endif
